
package com.urick.loadbanlance.dubbo;

import cn.hutool.core.collection.CollectionUtil;

import java.util.List;

/**
 * AbstractLoadBalance
 */
public abstract class AbstractLoadBalance implements ILoadBalance {
    /**
     * Calculate the weight according to the uptime proportion of warmup time
     * the new weight will be within 1(inclusive) to weight(inclusive)
     *
     * @param uptime the uptime in milliseconds
     * @param warmup the warmup time in milliseconds
     * @param weight the weight of an invoker
     * @return weight which takes warmup into account
     * <p>
     * 随着系统启动时间，以及设置预热时间，计算系统当前权重，也就是在预热时间内逐步放开流量
     * <p>
     * 避免系统启动时大量流量涌入，导致系统崩溃
     * <p>
     * 假设我们设置A服务的权重是5,让它预热10分钟， 则第一分钟的时候， 它的权重变为(1/10) X5 = 0.5, 0.5/5 = 0.1,也就是只承担10%的流量； 10分钟后，
     * 权重就变为(10/10) X5 = 5,也就是权重变为设置的100%,承担了所有的流量。
     */
    static int calculateWarmupWeight(long uptime, long warmup, int weight) {
        int ww = (int) (uptime / (warmup / weight));
        return ww < 1 ? 1 : (Math.min(ww, weight));
    }

    @Override
    public ServerInstance getServer(List<ServerInstance> instanceList) {
        if (CollectionUtil.isEmpty(instanceList)) {
            return null;
        }
        if (instanceList.size() == 1) {
            return instanceList.get(0);
        }
        return doSelect(instanceList);
    }

    protected abstract ServerInstance doSelect(List<ServerInstance> instanceList);

    /**
     * 获取服务权重，基于服务预热计算权重
     *
     * @param instance instance
     * @return instance
     */
    protected int getWeight(ServerInstance instance) {
        return getWeight(instance, true);
    }

    protected int getWeight(ServerInstance instance, boolean warmupCalculate) {
        if (!warmupCalculate) return instance.getWeight();
        int weight = instance.getWeight() == 0 ? DEFAULT_WEIGHT : instance.getWeight();
        if (weight > 0) {
            long timestamp = instance.getStartTime();//启动弄时间
            if (timestamp > 0L) {
                long uptime = System.currentTimeMillis() - timestamp;//当前系统运行时间
                if (uptime < 0) {
                    return 1;
                }
                //系统预热时间
                long warmup = instance.getWarmup() == 0 ? DEFAULT_WARMUP : instance.getWarmup();
                if (uptime > 0 && uptime < warmup) {//当系统运行时间小于预热阀值时动态计算权重
                    weight = calculateWarmupWeight(uptime, warmup, weight);
                }
            }
        }

        return Math.max(weight, 0);
    }
}
